package com.googlecode.cswish.struts.hibernate.ehcache;

import java.beans.PropertyChangeListener;
import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.Map;

import net.sf.ehcache.CacheException;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Ehcache;
import net.sf.ehcache.Element;
import net.sf.ehcache.Statistics;
import net.sf.ehcache.Status;
import net.sf.ehcache.bootstrap.BootstrapCacheLoader;
import net.sf.ehcache.config.CacheConfiguration;
import net.sf.ehcache.event.RegisteredEventListeners;
import net.sf.ehcache.exceptionhandler.CacheExceptionHandler;
import net.sf.ehcache.extension.CacheExtension;
import net.sf.ehcache.loader.CacheLoader;
import net.sf.ehcache.search.Attribute;
import net.sf.ehcache.search.Query;
import net.sf.ehcache.statistics.CacheUsageListener;
import net.sf.ehcache.statistics.LiveCacheStatistics;
import net.sf.ehcache.statistics.sampled.SampledCacheStatistics;
import net.sf.ehcache.terracotta.TerracottaNotRunningException;
import net.sf.ehcache.transaction.manager.TransactionManagerLookup;
import net.sf.ehcache.writer.CacheWriter;
import net.sf.ehcache.writer.CacheWriterManager;

import com.googlecode.cswish.struts.ContextHolder;
import com.googlecode.cswish.struts.hibernate.ISite;

/**
 * Wrapper for the Ehcache, convert it to the cross site cache
 * 
 * @author Jerry
 *
 */
public class SiteCache implements Ehcache {

	private EhCacheRegionFactory factory;
	private CacheManager cacheManager;
	private Ehcache cache;
	
	public SiteCache(EhCacheRegionFactory factory, Ehcache cache) {
		this.factory = factory;
		this.cacheManager = factory.getCacheManager();
		this.cache = cache;
	}

	public void put(Element element) throws IllegalArgumentException, IllegalStateException, CacheException {
		getCache().put(element);
	}

	public void put(Element element, boolean doNotNotifyCacheReplicators) throws IllegalArgumentException,
			IllegalStateException, CacheException {
		getCache().put(element, doNotNotifyCacheReplicators);
	}

	public void putQuiet(Element element) throws IllegalArgumentException, IllegalStateException, CacheException {
		getCache().putQuiet(element);
	}

	public void putWithWriter(Element element) throws IllegalArgumentException, IllegalStateException, CacheException {
		getCache().putWithWriter(element);
	}

	public Element putIfAbsent(Element element) throws NullPointerException {
		return getCache().putIfAbsent(element);
	}

	public boolean removeElement(Element element) throws NullPointerException {
		return getCache().removeElement(element);
	}

	public boolean replace(Element old, Element element) throws NullPointerException, IllegalArgumentException {
		return getCache().replace(old, element);
	}

	public Element replace(Element element) throws NullPointerException {
		return getCache().replace(element);
	}

	public Element get(Serializable key) throws IllegalStateException, CacheException {
		return getCache().get(key);
	}

	public Element get(Object key) throws IllegalStateException, CacheException {
		return getCache().get(key);
	}

	public Element getQuiet(Serializable key) throws IllegalStateException, CacheException {
		return getCache().getQuiet(key);
	}

	public Element getQuiet(Object key) throws IllegalStateException, CacheException {
		return getCache().getQuiet(key);
	}

	public List getKeys() throws IllegalStateException, CacheException {
		return getCache().getKeys();
	}

	public List getKeysWithExpiryCheck() throws IllegalStateException, CacheException {
		return getCache().getKeysWithExpiryCheck();
	}

	public List getKeysNoDuplicateCheck() throws IllegalStateException {
		return getCache().getKeysNoDuplicateCheck();
	}

	public boolean remove(Serializable key) throws IllegalStateException {
		return getCache().remove(key);
	}

	public boolean remove(Object key) throws IllegalStateException {
		return getCache().remove(key);
	}

	public boolean remove(Serializable key, boolean doNotNotifyCacheReplicators) throws IllegalStateException {
		return getCache().remove(key, doNotNotifyCacheReplicators);
	}

	public boolean remove(Object key, boolean doNotNotifyCacheReplicators) throws IllegalStateException {
		return getCache().remove(key, doNotNotifyCacheReplicators);
	}

	public boolean removeQuiet(Serializable key) throws IllegalStateException {
		return getCache().removeQuiet(key);
	}

	public boolean removeQuiet(Object key) throws IllegalStateException {
		return getCache().removeQuiet(key);
	}

	public boolean removeWithWriter(Object key) throws IllegalStateException, CacheException {
		return getCache().removeWithWriter(key);
	}

	public void removeAll() throws IllegalStateException, CacheException {
		getCache().removeAll();
	}

	public void removeAll(boolean doNotNotifyCacheReplicators) throws IllegalStateException, CacheException {
		getCache().removeAll(doNotNotifyCacheReplicators);
	}

	public void flush() throws IllegalStateException, CacheException {
		getCache().flush();
	}

	public int getSize() throws IllegalStateException, CacheException {
		return getCache().getSize();
	}

	public int getSizeBasedOnAccuracy(int statisticsAccuracy) throws IllegalArgumentException, IllegalStateException,
			CacheException {
		return getCache().getSizeBasedOnAccuracy(statisticsAccuracy);
	}

	public long calculateInMemorySize() throws IllegalStateException, CacheException {
		return getCache().calculateInMemorySize();
	}

	public long calculateOffHeapSize() throws IllegalStateException, CacheException {
		return getCache().calculateOffHeapSize();
	}

	public long getMemoryStoreSize() throws IllegalStateException {
		return getCache().getMemoryStoreSize();
	}

	public long getOffHeapStoreSize() throws IllegalStateException {
		return getCache().getOffHeapStoreSize();
	}

	public int getDiskStoreSize() throws IllegalStateException {
		return getCache().getDiskStoreSize();
	}

	public Status getStatus() {
		return getCache().getStatus();
	}

	public String getName() {
		return cache.getName();
	}

	public void setName(String name) {
		cache.setName(name);
	}

	public String toString() {
		return getCache().toString();
	}

	public boolean isExpired(Element element) throws IllegalStateException, NullPointerException {
		return getCache().isExpired(element);
	}

	public Object clone() throws CloneNotSupportedException {
		return getCache().clone();
	}

	public RegisteredEventListeners getCacheEventNotificationService() {
		return getCache().getCacheEventNotificationService();
	}

	public boolean isElementInMemory(Serializable key) {
		return getCache().isElementInMemory(key);
	}

	public boolean isElementInMemory(Object key) {
		return getCache().isElementInMemory(key);
	}

	public boolean isElementOnDisk(Serializable key) {
		return getCache().isElementOnDisk(key);
	}

	public boolean isElementOnDisk(Object key) {
		return getCache().isElementOnDisk(key);
	}

	public String getGuid() {
		return getCache().getGuid();
	}

	public CacheManager getCacheManager() {
		return getCache().getCacheManager();
	}

	public void clearStatistics() {
		getCache().clearStatistics();
	}

	public int getStatisticsAccuracy() {
		return getCache().getStatisticsAccuracy();
	}

	public void setStatisticsAccuracy(int statisticsAccuracy) {
		getCache().setStatisticsAccuracy(statisticsAccuracy);
	}

	public void evictExpiredElements() {
		getCache().evictExpiredElements();
	}

	public boolean isKeyInCache(Object key) {
		return getCache().isKeyInCache(key);
	}

	public boolean isValueInCache(Object value) {
		return getCache().isValueInCache(value);
	}

	public Statistics getStatistics() throws IllegalStateException {
		return getCache().getStatistics();
	}

	public LiveCacheStatistics getLiveCacheStatistics() throws IllegalStateException {
		return getCache().getLiveCacheStatistics();
	}

	public void registerCacheUsageListener(CacheUsageListener cacheUsageListener) throws IllegalStateException {
		getCache().registerCacheUsageListener(cacheUsageListener);
	}

	public void removeCacheUsageListener(CacheUsageListener cacheUsageListener) throws IllegalStateException {
		getCache().removeCacheUsageListener(cacheUsageListener);
	}

	public void setCacheManager(CacheManager cacheManager) {
		getCache().setCacheManager(cacheManager);
	}

	public BootstrapCacheLoader getBootstrapCacheLoader() {
		return getCache().getBootstrapCacheLoader();
	}

	public void setBootstrapCacheLoader(BootstrapCacheLoader bootstrapCacheLoader) throws CacheException {
		getCache().setBootstrapCacheLoader(bootstrapCacheLoader);
	}

	public void setDiskStorePath(String diskStorePath) throws CacheException {
		getCache().setDiskStorePath(diskStorePath);
	}

	public void initialise() {
		getCache().initialise();
	}

	public void bootstrap() {
		getCache().bootstrap();
	}

	public void dispose() throws IllegalStateException {
		getCache().dispose();
	}

	public CacheConfiguration getCacheConfiguration() {
		return getCache().getCacheConfiguration();
	}

	public void registerCacheExtension(CacheExtension cacheExtension) {
		getCache().registerCacheExtension(cacheExtension);
	}

	public void unregisterCacheExtension(CacheExtension cacheExtension) {
		getCache().unregisterCacheExtension(cacheExtension);
	}

	public List<CacheExtension> getRegisteredCacheExtensions() {
		return getCache().getRegisteredCacheExtensions();
	}

	public float getAverageGetTime() {
		return getCache().getAverageGetTime();
	}

	public void setCacheExceptionHandler(CacheExceptionHandler cacheExceptionHandler) {
		getCache().setCacheExceptionHandler(cacheExceptionHandler);
	}

	public CacheExceptionHandler getCacheExceptionHandler() {
		return getCache().getCacheExceptionHandler();
	}

	public void registerCacheLoader(CacheLoader cacheLoader) {
		getCache().registerCacheLoader(cacheLoader);
	}

	public void unregisterCacheLoader(CacheLoader cacheLoader) {
		getCache().unregisterCacheLoader(cacheLoader);
	}

	public List<CacheLoader> getRegisteredCacheLoaders() {
		return getCache().getRegisteredCacheLoaders();
	}

	public void registerCacheWriter(CacheWriter cacheWriter) {
		getCache().registerCacheWriter(cacheWriter);
	}

	public void unregisterCacheWriter() {
		getCache().unregisterCacheWriter();
	}

	public CacheWriter getRegisteredCacheWriter() {
		return getCache().getRegisteredCacheWriter();
	}

	public Element getWithLoader(Object key, CacheLoader loader, Object loaderArgument) throws CacheException {
		return getCache().getWithLoader(key, loader, loaderArgument);
	}

	public Map getAllWithLoader(Collection keys, Object loaderArgument) throws CacheException {
		return getCache().getAllWithLoader(keys, loaderArgument);
	}

	public void load(Object key) throws CacheException {
		getCache().load(key);
	}

	public void loadAll(Collection keys, Object argument) throws CacheException {
		getCache().loadAll(keys, argument);
	}

	public boolean isDisabled() {
		return getCache().isDisabled();
	}

	public void setDisabled(boolean disabled) {
		getCache().setDisabled(disabled);
	}

	public boolean isStatisticsEnabled() {
		return getCache().isStatisticsEnabled();
	}

	public void setStatisticsEnabled(boolean enableStatistics) {
		getCache().setStatisticsEnabled(enableStatistics);
	}

	public SampledCacheStatistics getSampledCacheStatistics() {
		return getCache().getSampledCacheStatistics();
	}

	public void setSampledStatisticsEnabled(boolean enableStatistics) {
		getCache().setSampledStatisticsEnabled(enableStatistics);
	}

	public boolean isSampledStatisticsEnabled() {
		return getCache().isSampledStatisticsEnabled();
	}

	public Object getInternalContext() {
		return getCache().getInternalContext();
	}

	public void disableDynamicFeatures() {
		getCache().disableDynamicFeatures();
	}

	public CacheWriterManager getWriterManager() {
		return getCache().getWriterManager();
	}

	public boolean isClusterCoherent() throws TerracottaNotRunningException {
		return getCache().isClusterCoherent();
	}

	public boolean isNodeCoherent() throws TerracottaNotRunningException {
		return getCache().isNodeCoherent();
	}

	public void setNodeCoherent(boolean coherent) throws UnsupportedOperationException, TerracottaNotRunningException {
		getCache().setNodeCoherent(coherent);
	}

	public void waitUntilClusterCoherent() throws UnsupportedOperationException, TerracottaNotRunningException {
		getCache().waitUntilClusterCoherent();
	}

	public void setTransactionManagerLookup(TransactionManagerLookup transactionManagerLookup) {
		getCache().setTransactionManagerLookup(transactionManagerLookup);
	}

	public void addPropertyChangeListener(PropertyChangeListener listener) {
		getCache().addPropertyChangeListener(listener);
	}

	public void removePropertyChangeListener(PropertyChangeListener listener) {
		getCache().removePropertyChangeListener(listener);
	}

	public <T> Attribute<T> getSearchAttribute(String attributeName) throws CacheException {
		return getCache().getSearchAttribute(attributeName);
	}

	public Query createQuery() {
		return getCache().createQuery();
	}

	public boolean isSearchable() {
		return getCache().isSearchable();
	}

	public long getAverageSearchTime() {
		return getCache().getAverageSearchTime();
	}

	public long getSearchesPerSecond() {
		return getCache().getSearchesPerSecond();
	}

	public void acquireReadLockOnKey(Object key) {
		getCache().acquireReadLockOnKey(key);
	}

	public void acquireWriteLockOnKey(Object key) {
		getCache().acquireWriteLockOnKey(key);
	}

	public boolean tryReadLockOnKey(Object key, long timeout) throws InterruptedException {
		return getCache().tryReadLockOnKey(key, timeout);
	}

	public boolean tryWriteLockOnKey(Object key, long timeout) throws InterruptedException {
		return getCache().tryWriteLockOnKey(key, timeout);
	}

	public void releaseReadLockOnKey(Object key) {
		getCache().releaseReadLockOnKey(key);
	}

	public void releaseWriteLockOnKey(Object key) {
		getCache().releaseWriteLockOnKey(key);
	}

	public boolean isReadLockedByCurrentThread(Object key) {
		return getCache().isReadLockedByCurrentThread(key);
	}

	public boolean isWriteLockedByCurrentThread(Object key) {
		return getCache().isWriteLockedByCurrentThread(key);
	}

	public boolean isClusterBulkLoadEnabled() throws UnsupportedOperationException, TerracottaNotRunningException {
		return getCache().isClusterBulkLoadEnabled();
	}

	public boolean isNodeBulkLoadEnabled() throws UnsupportedOperationException, TerracottaNotRunningException {
		return getCache().isNodeBulkLoadEnabled();
	}

	public void setNodeBulkLoadEnabled(boolean enabledBulkLoad) throws UnsupportedOperationException,
			TerracottaNotRunningException {
		getCache().setNodeBulkLoadEnabled(enabledBulkLoad);
	}

	public void waitUntilClusterBulkLoadComplete() throws UnsupportedOperationException, TerracottaNotRunningException {
		getCache().waitUntilClusterBulkLoadComplete();
	}
	
	public Ehcache getCache() {
		String siteName = ContextHolder.get().getSiteName();
		Ehcache siteCache;
		if (siteName != null && !ISite.DEFAULT_NAME.equals(siteName)) {
			String name = cache.getName();
			String newName = '#' + siteName + '#' + name;
			siteCache = cacheManager.getCache(newName);
			if (siteCache == null) {
				siteCache = cacheManager.getCache(name);
				
				if (siteCache != null) {
					Ehcache newCache = factory.createCache(name);
					newCache.setName(newName);
					cacheManager.addCache(newCache);
					siteCache = newCache;
				}
			}
		} else {
			siteCache = this.cache;
		}
		return siteCache;
	}

	public Ehcache getSiteCache(String siteName) {
		String newName = '#' + siteName + '#' + cache.getName();
		return cacheManager.getCache(newName);
	}
}